/* eslint-disable */
function constant (x) {
  return function () {
    return x
  }
}

export function ellipseForce (padding, innerRepulsion, outerRepulsion) {
  let nodes
  if (typeof padding !== 'function') padding = constant(padding == null ? 4 : +padding)
  innerRepulsion = innerRepulsion == null ? 0.5 : +innerRepulsion
  outerRepulsion = outerRepulsion == null ? 0.5 : +outerRepulsion
  function force (alpha) {
    let i, j, n = nodes.length,
      node, my_padding, my_w, my_h, my_x, my_y,
      my_w2, my_h2, my_wh,
      other, other_padding, other_w, other_h, other_x, other_y,
      dist_x, dist_y,
      force_ratio, dist, gap, repulsion, x_component, y_component,
      g, g2, x1, y1, x2, y2, d1, d2,
      force_ratio1, force_ratio2,
      myOuterRepulsion = outerRepulsion * 16;
    for (i = 0; i < n; ++i) {
      node = nodes[i]
      my_padding = +padding(node, i, nodes)
      my_w = node.rx + my_padding
      my_h = node.ry + my_padding
      my_w2 = my_w * my_w
      my_h2 = my_h * my_h
      my_wh = my_w * my_h
      my_x = node.x + node.vx
      my_y = node.y + node.vy
      for (j = 0; j < n; ++j) {
        if (j == i) {
          continue
        }
        other = nodes[j]
        other_padding = +padding(other, j, nodes)
        other_w = other.rx + other_padding
        other_h = other.ry + other_padding
        other_x = other.x + other.vx
        other_y = other.y + other.vy
        dist_x = my_x - other_x
        dist_y = my_y - other_y
        if (dist_x == 0 && dist_y == 0) {
          node.vx += (Math.random() * 4) - 2
          node.vy += (Math.random() * 4) - 2
          continue
        } else if (dist_x == 0) {
          force_ratio = (my_h / my_w + other_h / other_w) / 2
          dist = Math.abs(dist_y)
          gap = dist - my_h - other_h
        } else if (dist_y == 0) {
          force_ratio = 1
          dist = abs(dist_x)
          gap = dist - my_w - other_w
        } else {
          g = dist_y / dist_x
          g2 = g * g
          x1 = my_wh / Math.sqrt(my_h2 + g2 * my_w2)
          y1 = g * x1
          d1 = Math.sqrt(x1 * x1 + y1 * y1)
          force_ratio1 = d1 / my_w
          x2 = (other_w * other_h) / Math.sqrt(other_h * other_h + g2 * other_w * other_w)
          y2 = g * x2
          d2 = Math.sqrt(x2 * x2 + y2 * y2)
          force_ratio2 = d2 / other_w
          dist = Math.sqrt(dist_x * dist_x + dist_y * dist_y)
          gap = dist - d2 - d1
          force_ratio = (force_ratio1 + force_ratio2) / 2
        }
        x_component = dist_x / dist
        y_component = dist_y / dist
        if (gap < 0) {
          repulsion = Math.min(Math.max(1.0, innerRepulsion * force_ratio * -gap), 5.0)
          node.vx += repulsion * x_component
          node.vy += repulsion * y_component
        } else {
          repulsion = Math.min(20.0, (force_ratio * myOuterRepulsion * alpha) / gap)
          node.vx += repulsion * x_component
          node.vy += repulsion * y_component
        }
      }
    }
  }
  force.initialize = function (my_nodes) {
    nodes = my_nodes
  }
  force.outerRepulsion = function (my_outerRepulsion) {
    if (arguments.length) {
      outerRepulsion = +my_outerRepulsion
      return force
    } else {
      return outerRepulsion
    }
  }
  force.innerRepulsion = function (my_innerRepulsion) {
    if (arguments.length) {
      innerRepulsion = +my_innerRepulsion
      return force
    } else {
      return innerRepulsion
    }
  }
  force.padding = function (my_padding) {
    if (arguments.length) {
      if (typeof my_padding === 'function') {
        padding = my_padding
      } else {
        padding = constant(+my_padding)
      }
      return force
    } else {
      return padding
    }
  }
  return force
}
